package paper.http;

import java.util.HashMap;
import java.util.Random;
import java.security.MessageDigest;
import java.sql.*;

import paper.db.DB;

public class Authorisation {
    private static Random r = new Random();
    private static HashMap<String,Authorisation> current = new HashMap<String,Authorisation>();

    public String userID;
    public String givenName;
    public String surname; 
    public String affiliation;
    public String cookie;

    public Authorisation() {
    }

    public Authorisation(String cookie, String userID) {
        this.cookie = cookie;
        this.userID = userID;
    }

    public static Authorisation loadOnReset(String resetCookie) {
        Statement st = null;
        ResultSet rs = null;
        Authorisation a = null;
        try {
            st = DB.connection.createStatement();
            rs = st.executeQuery("SELECT * FROM users u WHERE resetpassword='" + DB.q(resetCookie,32) + "'");
            if (rs.next()) {
                a = new Authorisation();
                a.affiliation = rs.getString(DB.affiliation);
                a.surname     = rs.getString(DB.surname);
                a.givenName   = rs.getString(DB.givenName);
                a.userID      = rs.getString(DB.userID);
            }
            rs.close();
        } catch (Exception e) {
            System.err.println("Fuck... (17) " + e);
        } finally {
            try {
                if (rs != null) rs.close();
                if (st != null) st.close();
            } catch(Exception e) {
                System.err.println("Fuck..." + e);
            }
        }
        return a;
    }

    public static String resetPassword(String username) {
        String resetCookie = DB.randomCookie();

        Statement st = null;
        ResultSet rs = null;
        try {
            st = DB.connection.createStatement();
            rs = st.executeQuery("SELECT u.userid FROM users u, emails e WHERE e.email='" +
                                 DB.q(username,DB.emailMax) + "' and e.userid = u.userid");
            if (rs.next()) {
                String userID   = rs.getString(DB.userID);
                st.executeUpdate("UPDATE users SET " +
                                 "resetpassword='" + DB.q(resetCookie,DB.resetPasswordMax) + "' " +
                                 "WHERE userid = '" + DB.q(userID,DB.userIDMax)+ "'");
            } else {
                String newUserID   = DB.randomCookie();
                st.executeUpdate("INSERT INTO users (userID,resetpassword,password,affiliation,surname,givenname,cookie) VALUES ('"+newUserID+"','"+resetCookie+"','','','','','')");
                st.executeUpdate("INSERT INTO emails (userID,email) VALUES ('"+newUserID+"','"+username+"')");
            }
        } catch (Exception e) {
            System.err.println("Failed to set resetpassword " + username + " " + e);
            return null;
        } finally {
            try {
                if (rs != null) rs.close();
                if (st != null) st.close();
            } catch(Exception e) {
                System.err.println("Fuck..." + e);
            }
        }
        return resetCookie;
    }

    public void commitEntry(String plainPassword) {
        String password = null;
        if (!plainPassword.equals("")) {
            password = digestPassword(plainPassword);
        }
        Statement st = null;
        try {
            String md5 = digestPassword(password);
            st = DB.connection.createStatement();
            st.executeUpdate("UPDATE users SET " +
                             (password == null ? "" : "password='" + DB.q(password,DB.passwordMax) + "',") +
                             "surname='" + DB.q(surname,DB.surnameMax) + "'," +
                             "resetPassword=''," +
                             "givenname='" + DB.q(givenName,DB.givenNameMax) + "'," +
                             "affiliation='" + DB.q(affiliation,DB.affiliationMax) + "' " +
                             "WHERE userid = '" + DB.q(userID,DB.userIDMax)+ "'");
        } catch (Exception e) {
            System.err.println("Failed to commit password " + userID + " " + e);
        } finally {
            try {
                if (st != null) st.close();
            } catch(Exception e) {
                System.err.println("Fuck..." + e);
            }
        }
    }

    public static String digestPassword(String password) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(password.getBytes());
            byte[] digest = md.digest();
            StringBuilder md5Password = new StringBuilder(32);
            for (int i = 0; i < digest.length; i++) {
                String hexVal = Integer.toHexString(digest[i] & 255);
                md5Password.append(hexVal.length() < 2 ? "0" + hexVal : hexVal);
            }
            return md5Password.toString();
        } catch (Exception e) {
            System.err.println("Well, no MD5 " + e);
        }
        return null;
    }

    public static String cookieForPassword(String email, String password) {
        String cookie = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            String md5 = digestPassword(password);
            System.out.println("Get cookie " + email + " " + md5);
            st = DB.connection.createStatement();
            rs = st.executeQuery("SELECT u.password, u.userid FROM users u, emails e WHERE e.email='" +
                                 DB.q(email,DB.emailMax) + "' and e.userid = u.userid");
            if (!rs.next()) {
                System.err.println("No password for " + email + " exists");
                return null;
            }
            if (!md5.equals(rs.getString(DB.password))) {
                System.err.println("Password of " + email + " does not match");
                return null;
            }
            String userID = rs.getString(DB.userID);
            cookie = DB.randomCookie();
            current.put(email, new Authorisation(cookie,userID));
            st.executeUpdate("UPDATE users SET cookie='" + cookie +
                             "' WHERE userid='" + userID + "'");

        } catch (Exception e) {
            System.err.println("Failed to fetch password " + email + " " + e);
            return null;
        } finally {
            try {
                if (rs != null) rs.close();
                if (st != null) st.close();
            } catch(Exception e) {
                System.err.println("Fuck..." + e);
            }
        }
        return cookie;
    }

    public static String userIDOfCookie(String email, String cookie) {
        Authorisation auth = current.get(email);
        if (auth != null) {
            if (auth.cookie.equals(cookie)) {
                return auth.userID;   // Ok, cookie matches cache entry
            }
            current.remove(email); // Did not match cache - remove cached entry
            auth = null;
        }
        System.out.println("Get cookie for " + email);
        Statement st = null;
        ResultSet rs = null;
        try {
            st = DB.connection.createStatement();
            rs = st.executeQuery("SELECT u.cookie, u.userid FROM users u, emails e WHERE e.email='" +
                                 DB.q(email, DB.emailMax) + "' and e.userid = u.userid");
            if (!rs.next()) {
                return null;
            }
            String dbCookie = rs.getString(DB.cookie);
            String userID =   rs.getString(DB.userID);
            if (dbCookie.equals(cookie)) {
                current.put(email, new Authorisation(cookie,userID));
                return userID;   // Ok, found it in database, added to cache
            }
            st.executeUpdate("UPDATE users SET cookie='' WHERE userid='"
                             + userID + "'");  // db cookie was wrong.
        } catch (SQLException e) {
            System.err.println("Failed to fetch cookie " + email + " " + e);
        } finally {
            try {
                if (rs != null) rs.close();
                if (st != null) st.close();
            } catch(Exception e) {
                System.err.println("Fuck..." + e);
            }
        }
        return null;
    }

    // add expiry thread, with second hashmap, moving current to old every X hours. And load from DB on demand.
}

